feat: RealUnit wallet-to-wallet (W2W) gasless transfer flow#687
Open
TaprootFreak wants to merge 5 commits into
Open
feat: RealUnit wallet-to-wallet (W2W) gasless transfer flow#687TaprootFreak wants to merge 5 commits into
TaprootFreak wants to merge 5 commits into
Conversation
e4925f9 to
183b7a4
Compare
cd36446 to
532d1cc
Compare
Add a wallet-to-wallet send flow that transfers REALU to another wallet (recipient via QR scan or manual entry), consuming the gasless EIP-7702 transfer endpoints from DFXswiss/api#3820. The transfer is gasless: DFX pays gas from a dedicated W2W gas wallet via EIP-7702, so the app signs an EIP-712 delegation + an EIP-7702 authorization exactly like the SOFTWARE gasless sell confirm — reusing eip712_signer.dart / eip7702_signer.dart and the wallet unlock/lock boundary. The capability gate (software-only signing) surfaces a dedicated unsupported state for debug/BitBox wallets; the flow is not otherwise branched on wallet type. - Steps: enter/scan recipient -> amount (whole REALU shares, vs available balance) -> confirm -> sign + PUT /transfer then /transfer/:id/confirm -> success (txHash) / typed failures (unsupported wallet, signature cancelled, invalid request, gas-funding-unavailable from the API 503). - Page + Cubit per step with separate state files. - Reuse the QR scanner: extract a shared QrScannerView wrapping MobileScanner and refactor the pay scan page onto it (no scanner duplication). - RealUnitTransferService (extends DFXAuthService) with the two endpoints, fromJson DTOs, and typed exceptions enumerated in exception_surface_test. - Navigation: AppRoutes.send + GoRoute('/send') + a dashboard Send action. - i18n: new keys in both ARBs, German + English. - bloc_test/mocktail cubit tests for every step + typed failure, widget tests for the pages, 100% scoped coverage, golden tests + baselines.
Add widget/unit tests for the changed lib lines that the existing pay + send suites did not yet exercise: - DashboardActions: render + tap-routes the buy/sell/pay/send action buttons, covering the four Expanded(ActionButton) subtrees and their onPressed push closures (incl. the new send button). - setupServices: resolve the newly registered RealUnitPayService and RealUnitTransferService factories, covering their registration and construction closures in di.dart. - routerConfig /pay and /send routes: drive the real router to each route so the GoRoute builder closures returning PayScanPage / SendRecipientPage are executed. AppRoutes.pay and AppRoutes.send are compile-time const fields (no instrumentable line); they are exercised at runtime by the above tests.
Regenerate the three dashboard goldens that include the action row so they show the four-button layout (buy/sell/pay/send). Baselines taken from the self-hosted VR runner output (testImage).
532d1cc to
63743cc
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Phase 2 Baustein 3 — RealUnit wallet-to-wallet (W2W) transfer: send REALU to another wallet, recipient picked via QR scan or manual entry. Implements #684 (umbrella #666).
The transfer is gasless via EIP-7702 — DFX pays gas from a dedicated W2W gas wallet — so the app signs an EIP-712 delegation + an EIP-7702 authorization, exactly like the existing SOFTWARE gasless sell confirm (
real_unit_sell_payment_info_service.dart). It reuseseip712_signer.dart/eip7702_signer.dartand the wallet unlock/lock boundary; it is not the bitbox raw-tx path.Flow (Page + Cubit per step, separate state files):
ethereum:EIP-681 URI is normalized to the bare address.decimals = 0); the available balance is read via the shared balance watcher and the over-balance guard is local UX only.PUT /transfer→ sign EIP-712 delegation + EIP-7702 authorization →PUT /transfer/:id/confirm→ success (txHash) / typed failure.Typed failures rendered as states (no error-string parsing): unsupported wallet (debug/BitBox), signature cancelled, invalid request (API 400/404 — invalid recipient / self-transfer / token-contract recipient / insufficient REALU), and gas-funding-unavailable (API
ServiceUnavailable503 → friendly "temporarily unavailable", REALU untouched).Scanner reuse (no duplication)
The scanner from #674 was an inline
MobileScannerinPayScanPage. Extracted a sharedlib/widgets/scanner/qr_scanner_view.dart(the camera/MethodChannel wrapper) and refactored both the pay scan page and the new send recipient page onto it — each flow keeps its own decode logic (LNURL vs EVM address). No scanner code is duplicated.API / decision authority
Consumes DFXswiss/api#3820 (pair-PR, backend lands first):
PUT /v1/realunit/transfer+PUT /v1/realunit/transfer/:id/confirm. The app renders API-signaled outcomes and does not duplicate backend KYC/registration/limit/eligibility logic.Branch / stacking
Branched from
feature/ocp-pay-flow(#674) to reuse the scanner without duplication; PR base isstaging. Stacked on #674 (scanner) — review/merge #674 first; this diff will shrink once #674 merges to staging.Tests / gates
flutter analyze: 0 issues.flutter test --coverage --exclude-tags golden: all pass; 100% scoped coverage on every new file (nocoverage:ignore).flutter test --tags golden: golden tests + baselines for the new screens.dart format(repo config: page_width 100, trailing_commas preserve): clean.Goldens regenerate pending on the runner: baselines here were rendered locally on macOS and will mismatch the CI runner; the Golden Regenerate workflow is being dispatched so the runner pushes authoritative baselines.
Stays Draft (no ready-for-review, no merge).